﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using iTextSharp.text;
using iTextSharp.text.pdf;
using Font = iTextSharp.text.Font;
using System.Reflection;
using System.ComponentModel;
using System.Web;
using System.Data;
using Winista.Text.HtmlParser;
using Winista.Text.HtmlParser.Filters;
using System.Text;
using Winista.Text.HtmlParser.Util;

namespace Common.Modules
{
    public class PdfMaker
    {
        //// 正文字体
        //private readonly Font _contentFont;

        //// 封面字体大小
        //internal const float CoverParaFontSize = 13;

        private Document _document;

        private readonly PdfElementGenerater _pdfElemGen = new PdfElementGenerater();

        private MemoryStream _memoryStream = new MemoryStream();

        private readonly PdfTableCss _pdfTableCss;
        private readonly ParagraphCss _tableTitleCss;
        private readonly ParagraphCss _chapterCcss;
        private readonly ParagraphCss _sectionCss;
        private readonly ParagraphCss _contentCss;
        private readonly ImageCss _imageCss;

        private int _chapterNum = 0;
        private int _sectionNum = 0;
        private int _tableNum = 0;
        private int _graphNum = 0;

        public PdfMaker()
        {
            FontFactory.RegisterDirectory(AppDomain.CurrentDomain.BaseDirectory + "\\Modules\\Fonts");
            _pdfTableCss = new PdfTableCss()
            {
                TitleCellCss = new CellCss()
                    {
                        Font = FontFactory.GetFont("STSONG", BaseFont.IDENTITY_H,
                            BaseFont.NOT_EMBEDDED, 10, Font.NORMAL, BaseColor.WHITE),
                        BackGroundColor = new BaseColor(93, 123, 157),
                        Alignment = Element.ALIGN_CENTER,
                        PaddingTop = null,
                        PaddingBottom = null,
                        Border = null,
                        BorderColor = BaseColor.BLACK
                    },
                BodyCellCsses = new CellCss[] { 
                    new CellCss()
                    {
                        Font = FontFactory.GetFont("STSONG", BaseFont.IDENTITY_H,
                            BaseFont.NOT_EMBEDDED, 10, Font.NORMAL, new BaseColor(40, 71, 117)),
                        BackGroundColor = BaseColor.WHITE,
                        Alignment = Element.ALIGN_CENTER,
                        PaddingTop = null,
                        PaddingBottom = null,
                        Border = null,
                        BorderColor = BaseColor.BLACK
                    },
                    new CellCss()
                    {
                        Font = FontFactory.GetFont("STSONG", BaseFont.IDENTITY_H,
                            BaseFont.NOT_EMBEDDED, 10, Font.NORMAL, new BaseColor(51, 51, 51)),
                        BackGroundColor = new BaseColor(247, 246, 243),//#F7F6F3
                        Alignment = Element.ALIGN_CENTER,
                        PaddingTop = null,
                        PaddingBottom = null,
                        Border = null,
                        BorderColor = BaseColor.BLACK
                    }
                },
                Alignment = Element.ALIGN_CENTER,
                SpacingBefore = null,
                SpacingAfter = null
            };
            _tableTitleCss = new ParagraphCss()
            {
                Font = FontFactory.GetFont(
                "STSONG", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, 10, Font.NORMAL, BaseColor.BLACK),
                Alignment = Element.ALIGN_CENTER,
                SpaceBefore = 10f,
                SpaceAfter = 20f
            };
            _chapterCcss = new ParagraphCss()
            {
                Font = FontFactory.GetFont(
                "STSONG", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, 17, Font.NORMAL, BaseColor.BLACK),
                Alignment = Element.ALIGN_LEFT
            };
            _sectionCss = new ParagraphCss()
            {
                Font = FontFactory.GetFont(
                "STSONG", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, 15, Font.NORMAL, BaseColor.BLACK),
                Alignment = Element.ALIGN_LEFT
            };
            _contentCss = new ParagraphCss()
            {
                Font = FontFactory.GetFont(
                "STSONG", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, 10, Font.NORMAL, BaseColor.BLACK),
                Alignment = Element.ALIGN_JUSTIFIED
            };
            _imageCss = new ImageCss()
            {
                Alignment = Element.ALIGN_CENTER,
                Width = 400f,
                Height = 300f
            };
        }

        public void Initialize()
        {
            _document = new Document(PageSize.A4, 50, 50, 50, 50);
            string fileName = string.Format("体检报告[{0},{1}].pdf", "123321123", "王强其");
            string PdfFillFullName = string.Format("{0}//{1}", @"D:", fileName);
            //var pdfWriter = PdfWriter.GetInstance(_document, _memoryStream);
            var pdfWriter = PdfWriter.GetInstance(_document, new FileStream(PdfFillFullName, FileMode.Create));
            var pdfEvent = new PdfEvent();
            pdfEvent.SetHeader("贝壳校医院", "BH000111", "王强其", "男");
            pdfWriter.PageEvent = pdfEvent;
            _document.Open();
        }

        public void SavePdf()
        {
            _document.Close();
        }

        #region Add table, cover, paragraph, so on

        public PdfPTable AddPdfTable<TModel>(IList<TModel> models, string tableName, float[] widths, params Expression<Func<TModel, object>>[] selector)
            where TModel : IEntity
        {
            if (tableName != null)
            {
                _pdfElemGen.AddSimpleParagraph(_document,
                    string.Format("表{0}  {1}", ++_tableNum, tableName), _tableTitleCss);
            }

            var pdfTable = _pdfElemGen.MakeTable(_document, _pdfTableCss, widths);
            InsertTitle(pdfTable, selector);
            foreach (var model in models)
            {
                InsertRow(pdfTable, model, selector);
            }

            _document.Add(pdfTable);
            return pdfTable;
        }

        private void InsertTitle<TModel>(PdfPTable pdfTable, params Expression<Func<TModel, object>>[] selector)
        {
            var props = selector.Select(ParseProp).ToList();
            for (int i = 0; i < props.Count; i++)
            {
                string prop = props[i];
                _pdfElemGen.AddCellToTable(pdfTable, prop, _pdfTableCss.TitleCellCss);
            }
        }

        private void InsertRow<TModel>(PdfPTable pdfTable, TModel model, params Expression<Func<TModel, object>>[] selector)
        {
            int rowIndex = pdfTable.Rows.Count;
            var body_color_num = _pdfTableCss.BodyCellCsses.Length;
            var props = selector.Select(m => ParsePropValue(model, m)).ToList();
            for (int i = 0; i < props.Count; i++)
            {
                string prop = props[i];
                _pdfElemGen.AddCellToTable(pdfTable, prop, _pdfTableCss.BodyCellCsses[rowIndex % body_color_num]);
            }
        }

        public PdfPTable AddPdfTable(DataTable dataTable, float[] widths)
        {
            if (!string.IsNullOrEmpty(dataTable.TableName))
            {
                _pdfElemGen.AddSimpleParagraph(_document,
                    string.Format("表{0}  {1}", ++_tableNum, dataTable.TableName), _tableTitleCss);
            }

            var pdfTable = _pdfElemGen.MakeTable(_document, _pdfTableCss, widths);
            foreach (DataColumn column in dataTable.Columns)
            {
                _pdfElemGen.AddCellToTable(pdfTable, column.ColumnName, _pdfTableCss.TitleCellCss);
            }

            foreach (DataRow row in dataTable.Rows)
            {
                int rowIndex = pdfTable.Rows.Count;
                var body_color_num = _pdfTableCss.BodyCellCsses.Length;
                for (int i = 0; i < dataTable.Columns.Count; i++)
                {
                    string propValue = row[i] == null ? "" : row[i].ToString();
                    _pdfElemGen.AddCellToTable(pdfTable, propValue, _pdfTableCss.BodyCellCsses[rowIndex % body_color_num]);
                }
            }

            _document.Add(pdfTable);
            return pdfTable;
        }

        public void AddStudentCover(string studentName, string institution)
        {
            var logo = Image.GetInstance(AppDomain.CurrentDomain.BaseDirectory + "images\\logo.jpg");
            logo.SpacingAfter = 10;
            _document.Add(logo);

            _pdfElemGen.AddSimpleParagraph(_document, string.Format("尊敬的{0}：", studentName), _contentCss);
            _pdfElemGen.AddSimpleParagraph(_document, "　　您好！", _contentCss);
            _pdfElemGen.AddSimpleParagraph(_document, string.Format("　　感谢您对{0}的信赖和支持。现将您考试报告呈上。", institution), _contentCss);
            _pdfElemGen.AddSimpleParagraph(_document, "　　本健康体检报告分为八个部分：", _contentCss);
            _pdfElemGen.AddSimpleParagraph(_document, "　　第一部分 综合分析", _contentCss);
            _pdfElemGen.AddSimpleParagraph(_document, "　　第二部分 xxx", _contentCss);
            _pdfElemGen.AddSimpleParagraph(_document, "　　第三部分 xxx", _contentCss);
            _pdfElemGen.AddSimpleParagraph(_document, "　　第四部分 xxx", _contentCss);
            _pdfElemGen.AddSimpleParagraph(_document, "　　第五部分 xxx", _contentCss);
            _pdfElemGen.AddSimpleParagraph(_document, "　　第六部分 xxx", _contentCss);
            _pdfElemGen.AddSimpleParagraph(_document, "　　第七部分 xxx", _contentCss);
            _pdfElemGen.AddSimpleParagraph(_document, "　　第八部分 xxx", _contentCss);
            _pdfElemGen.AddSimpleParagraph(_document, string.Format("{0}全体员工祝您学习进步，身体健康，全家幸福！", institution), _contentCss);
            _pdfElemGen.AddSimpleParagraph(_document, "　　", _contentCss);

            {
                //var pdfPTable = _pdfElemGen.MakeTable(_document, new[] { 30f, 60f });
                //pdfPTable.TotalWidth -= 200;
                //AddCellToTable(pdfPTable, "档案号　", _contentFont, LabelFontSize, Element.ALIGN_RIGHT, 10, 4, Rectangle.NO_BORDER, new BaseColor(0, 0, 0));
                //AddCellToTable(pdfPTable, gerentijian.体检档案编号, _kaitiFont, LabelFontSize, Element.ALIGN_CENTER, 10, 4, Rectangle.BOTTOM_BORDER);
                //AddCellToTable(pdfPTable, "姓　名　", _contentFont, LabelFontSize, Element.ALIGN_RIGHT, 10, 4, Rectangle.NO_BORDER, new BaseColor(0, 0, 0));
                //AddCellToTable(pdfPTable, tijianren.姓名, _kaitiFont, LabelFontSize, Element.ALIGN_CENTER, 10, 4, Rectangle.BOTTOM_BORDER);
                //AddCellToTable(pdfPTable, "性　别　", _contentFont, LabelFontSize, Element.ALIGN_RIGHT, 10, 4, Rectangle.NO_BORDER, new BaseColor(0, 0, 0));
                //AddCellToTable(pdfPTable, tijianren.性别, _kaitiFont, LabelFontSize, Element.ALIGN_CENTER, 10, 4, Rectangle.BOTTOM_BORDER);
                //AddCellToTable(pdfPTable, "年　龄　", _contentFont, LabelFontSize, Element.ALIGN_RIGHT, 10, 4, Rectangle.NO_BORDER, new BaseColor(0, 0, 0));
                //int age = gerentijian.年龄;
                //string strAge = age > 150 || age < 0 ? "" : age.ToString();
                //AddCellToTable(pdfPTable, string.Format("{0} 岁", strAge), _kaitiFont, LabelFontSize, Element.ALIGN_CENTER, 10, 4, Rectangle.BOTTOM_BORDER);
                //AddCellToTable(pdfPTable, "单　位　", _contentFont, LabelFontSize, Element.ALIGN_RIGHT, 10, 4, Rectangle.NO_BORDER, new BaseColor(0, 0, 0));
                //AddCellToTable(pdfPTable, tijianren.单位 == null ? "" : tijianren.单位.单位名称, _kaitiFont, LabelFontSize, Element.ALIGN_CENTER, 10, 4, Rectangle.BOTTOM_BORDER);
                //AddCellToTable(pdfPTable, "体检单位", _contentFont, LabelFontSize, Element.ALIGN_RIGHT, 10, 4, Rectangle.NO_BORDER, new BaseColor(0, 0, 0));
                //AddCellToTable(pdfPTable, yiyuanName, _kaitiFont, LabelFontSize, Element.ALIGN_CENTER, 10, 4, Rectangle.BOTTOM_BORDER);
                //AddCellToTable(pdfPTable, "体检时间", _contentFont, LabelFontSize, Element.ALIGN_RIGHT, 10, 4, Rectangle.NO_BORDER, new BaseColor(0, 0, 0));
                //AddCellToTable(pdfPTable, gerentijian.时间.ToString("yyyy-MM-dd"), _kaitiFont, LabelFontSize, Element.ALIGN_CENTER, 10, 4, Rectangle.BOTTOM_BORDER);
                //_document.Add(pdfPTable);
            }
            //_pdfElemGen.AddSimpleParagraph(_document, "倡导健康的生活方式，远离疾病！", _contentFont, 25, Element.ALIGN_CENTER, 60);

            _document.NewPage();

        }

        public void AddHeader(string title)
        {
            ParagraphCss pcss = new ParagraphCss()
            {
                Font = FontFactory.GetFont(
                "STSONG", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, 25, Font.NORMAL, BaseColor.BLACK),
                Alignment = Element.ALIGN_CENTER
            };
            _pdfElemGen.AddSimpleParagraph(_document, title, pcss);
        }

        public void AddSimpleParagraph(string inputHtml)
        {
            StringBuilder text = new StringBuilder();
            Parser parser = new Parser();
            parser.InputHTML = inputHtml;
            parser.Encoding = Encoding.UTF8.ToString();
            NodeList nodes = parser.ExtractAllNodesThatMatch(new StringFilter() { });
            for (int i = 0; i < nodes.Count; i++)
            {
                INode nodet = nodes.ElementAt(i);
                text.Append(nodet.ToPlainTextString().Replace("&nbsp;", " "));
            }
            string parsedText = text.ToString();
            _pdfElemGen.AddSimpleParagraph(_document, parsedText, _contentCss);
        }

        public void AddChapter(string chapterTxt)
        {
            ////chapter 对内存消耗大，不建议用此
            //var paragraph = new Paragraph(chapterTxt, _chapterCcss.Font);
            //var chapter = new Chapter(paragraph, _chapterNum);
            //_document.Add(chapter);
            //_currChapter = chapter;

            _pdfElemGen.AddSimpleParagraph(_document, string.Format("{0}  {1}", ++_chapterNum, chapterTxt),
                _chapterCcss);
            _sectionNum = 0;
        }

        public void AddSection(string sectionText, Chapter chapter = null)
        {
            //var title = new Paragraph(sectionText, _sectionCss.Font);
            //var cpt = chapter ?? _currChapter;
            //Section subsection = cpt.AddSection(20f, title, cpt.Count + 1);

            var paragraph = new Paragraph(sectionText, _sectionCss.Font);
            _pdfElemGen.AddSimpleParagraph(_document,
                string.Format("{0}.{1}  {2}", _chapterNum, ++_sectionNum, sectionText),
                _sectionCss);
        }

        public void AddGraph(System.Drawing.Image image, string imageTitle = null)
        {
            if (!string.IsNullOrEmpty(imageTitle))
            {
                _pdfElemGen.AddSimpleParagraph(_document,
                    string.Format("图{0}  {1}", ++_graphNum, imageTitle), _tableTitleCss);
            }
            _pdfElemGen.AddImage(_document, image, _imageCss);
        }

        #endregion

        #region parse prop and prop value

        private string ParseProp<TModel>(Expression<Func<TModel, object>> selector)
        {
            MemberExpression expression = selector.Body as MemberExpression;
            if (expression == null)
            {
                return null;
            }
            string propName = expression.Member.Name;
            PropertyInfo prop = typeof(TModel).GetProperty(propName);
            object[] attrs = prop.GetCustomAttributes(false);
            foreach (object attr in attrs)
            {
                if (attr is DisplayNameAttribute)
                {
                    return ((DisplayNameAttribute)attr).DisplayName;
                }
            }
            return propName;
        }

        private string ParsePropValue<TModel>(TModel model, Expression<Func<TModel, object>> selector)
        {
            MemberExpression expression = selector.Body as MemberExpression;
            if (expression == null)
            {
                return null;
            }
            string propName = expression.Member.Name;
            PropertyInfo prop = typeof(TModel).GetProperty(propName);
            return prop.GetValue(model, null) as string;
        }

        #endregion
    }
}
